#ifndef MATRIX_H
#define MATRIX_H

#include <iostream>
#include <vector>
#include <math.h>
#include "fraction.h"

using namespace std;

template <typename T>
class Matrix
{
private:
public:
    vector<vector<T>> mat;
    int nrows, ncols;
    Matrix() {}

    Matrix(int r, int c)
    {
        nrows = r;
        ncols = c;
        for (int i = 0; i < nrows; i++)
        {
            vector<T> vtmp;
            for (int j = 0; j < ncols; j++)
            {
                T tmp;
                vtmp.push_back(tmp);
            }
            mat.push_back(vtmp);
        }
    }

    Matrix(const Matrix<T> &m)
    {
        nrows = m.nrows;
        ncols = m.ncols;
        mat = m.mat;
    }

    Matrix(vector<vector<T>> &vec)
    {
        mat = vec;
        nrows = vec.size();
        ncols = vec[0].size();
    }

    ~Matrix()
    {
        mat.clear();
    }

    Matrix<T> operator=(const Matrix<T> &m2)
    {
        nrows = m2.nrows;
        ncols = m2.ncols;
        mat = m2.mat;
        return *this;
    }

    Matrix<T> operator+(const Matrix<T> &m2)
    {
        for (int i = 0; i < nrows; ++i)
        {
            for (int j = 0; j < ncols; ++j)
            {
                mat[i][j] = mat[i][j] + m2.mat[i][j];
            }
        }

        return *this;
    }

    Matrix<T> operator*(const T &scalar)
    {
        Matrix<T> result(nrows, ncols);

        for (unsigned int i = 0; i < nrows; ++i)
        {
            for (unsigned int j = 0; j < ncols; ++j)
            {
                result.mat[i][j] = mat[i][j] * scalar;
            }
        }

        return result;
    }

    Matrix<T> operator*(const Matrix<T> &m2)
    {
        Matrix<T> result(nrows, m2.ncols);

        if (ncols != m2.nrows)
            cout << "ERROR" << endl;
        for (int i = 0; i < nrows; i++)
        {
            for (int j = 0; j < m2.ncols; j++)
            {
                for (int k = 0; k < m2.nrows; k++)
                {
                    result.mat[i][j] += mat[i][k] * m2.mat[k][j];
                }
            }
        }

        return result;
    }

    T element(int i, int j)
    {
        return mat[i][j];
    }

    friend std::ostream &operator<<(std::ostream &os, Matrix<T> &A)
    {
        for (int i = 0; i < A.nrows; i++)
        {
            os << "[ " << A.element(i, 0) << "\t";
            for (int j = 1; j < A.ncols; j++)
                os << ", " << A.element(i, j) << "\t";
            os << " ]" << std::endl;
        }
        return os;
    }

    friend Matrix solve(Matrix A, Matrix b)
    {
        int n = A.ncols;
        Matrix x(n, 1);

        return x;
    }
};
#endif